package cn.yotta.user.web.autoconfiguration.utils;

import cn.yotta.common.utils.param.MoreObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.collect.Maps;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.UtilityClass;
import org.apache.commons.collections4.MapUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;

/**
 * @author wyaoyao
 * @date 2022/11/9 14:28
 * jwt工具类封装
 */
@UtilityClass
public class JwtUtil {

    private static final int DEFAULT_EXPIRE_SECOND = 60;

    private static final String DEFAULT_ISSUER = "Yotta";

    private static final String SECRET = "Yotta";

    /**
     * 默认过期时间容忍量
     */
    private static final Integer DEFAULT_ACCEPT_LEEWAY = 1;

    private static final Algorithm ALGORITHM = buildAlgorithm();

    private static Algorithm buildAlgorithm() {
        return Algorithm.HMAC256(SECRET);
    }

    public static String generateToken(JwtTokenGenParam param) {

        Date currentTime = new Date();
        // 计算过期时间
        Calendar instance = GregorianCalendar.getInstance();
        instance.setTime(currentTime);
        instance.add(Calendar.SECOND, MoreObject.firstNonNull(param.getExpireSecond(), DEFAULT_EXPIRE_SECOND));
        final String issuer = MoreObject.firstNotBlank(param.getIssuer(), DEFAULT_ISSUER);

        final JWTCreator.Builder builder = JWT.create()

                // 设置过期时间
                .withExpiresAt(instance.getTime())
                .withIssuedAt(currentTime)
                .withIssuer(issuer)
                .withSubject(param.getSubject());
        if (MapUtils.isNotEmpty(param.getClaims())) {
            param.getClaims().forEach(builder::withClaim);
        }
        // 加密生成token
        return builder.sign(ALGORITHM);
    }


    /**
     * @param parseParam jwt token 解析参数
     * @return 解析结果
     */
    public static JwtParseResult decode(JwtTokenParseParam parseParam) {
        final JWTVerifier verifier = JWT.require(ALGORITHM)
                .withIssuer(MoreObject.firstNotBlank(parseParam.getIssuer(), DEFAULT_ISSUER))
                .withSubject(parseParam.getSubject())
                .acceptLeeway(parseParam.getAcceptLeeway())
                .build();
        final DecodedJWT decodedJWT = verifier.verify(parseParam.getToken());
        final Map<String, String> claims = Maps.newHashMap();
        decodedJWT.getClaims().forEach((k,claim) ->{
            claims.put(k, claim.asString());
        });
        return JwtParseResult.builder()
                .isExpired(decodedJWT.getExpiresAt().getTime() <= System.currentTimeMillis())
                .issuer(decodedJWT.getIssuer())
                .subject(decodedJWT.getSubject())
                .claims(claims)
                .build();
    }


    @Builder
    @Data
    public static class JwtTokenGenParam {
        private Integer expireSecond;
        private String issuer;
        private String subject;
        private Map<String, String> claims;
    }


    @Getter
    public static class JwtTokenParseParam {
        @Setter
        private String issuer;
        private final String subject;
        private final String token;
        /**
         * 过期时间的容忍量（单位：秒）
         */
        private final Integer acceptLeeway;

        public JwtTokenParseParam(String token, String subject, Integer acceptLeeway) {
            this.token = token;
            this.subject = subject;
            this.acceptLeeway = acceptLeeway;
        }

        public JwtTokenParseParam(String token, String subject) {
            this(token, subject, DEFAULT_ACCEPT_LEEWAY);
        }
    }


    @Data
    @Builder
    public static class JwtParseResult {
        private String issuer;
        private String subject;
        private Map<String, String> claims;
        private Boolean isExpired = Boolean.FALSE;
    }


}
